/*
Copyright (c) [2019年5月1日] [吴超]
[MBT_Studio] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
		 http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/
#include "stdafx.h"
#include "时间轴.h"
#include "A_引擎/文件工程/工程文件.h"

#include <Context/节点数据操作.h>



static bool		g开始移动视口 = false;
static bool		g开始移动帧 = false;

std::set<C_时间轴*>					C_时间轴::g_当前时间轴页面;
static std::set<C_时间轴状态条*>	g_时间轴状态条;



bool f_时间轴_刷新绘制() {

	for (auto& e : C_时间轴::g_当前时间轴页面) {
		if (!e->m_被裁剪) {
			
			e->mui_状态条->m_当前帧.m_UI->m_Set(e->mui_状态条->m_当前帧);
			e->mui_状态条->m_帧区间.m_UI->m_Set(e->mui_状态条->m_帧区间);
		}
		e->m_更新绘制属性 = true;
	}

	for (auto& e : g_时间轴状态条) {
		e->m_更新绘制属性 = true;
	}

	auto scene = f_NodeCtx_get默认场景();
	f_render_刷新渲染(scene);
	
	return true;
}

static void f_当前帧PropUpdate(S_Props& prop) {
	f_时间轴_刷新绘制();
	f_工作区_联动更新(E_工作区类型::e_时间轴, E_工作区操作类型::e_所有);
}

static void on_播放控制(S_Props& prop) {
	C_状态条* tool = dynamic_cast<C_状态条*>(prop.m_UI->m_部件->m_父部件);

	switch (f_prop_enum(prop)) {
		case 0: {
			S_框架::g_播放帧 = false;
			f_时间工作区_帧步进(-1);
			f_时间轴_刷新绘制();
			break;
		}
		case 1: {
			S_框架::g_播放帧 = false;
			f_node_帧更新(S_框架::g_播放帧);
			break;
		}
		case 2: {
			S_框架::g_播放帧 = true;
			f_node_帧更新(S_框架::g_播放帧);
			break;
		}
		case 3: {
			S_框架::g_播放帧 = false;
			f_时间工作区_帧步进(1);
			f_时间轴_刷新绘制();
			break;
		}
		default:
			break;
	}
	f_工作区_联动更新(E_工作区类型::e_时间轴, E_工作区操作类型::e_所有);
}

static void on_清除物理引擎缓存(S_Props& prop) {
	for (auto it = S_节点数据::g_物理引擎.begin(); it != S_节点数据::g_物理引擎.end(); ++it) {
		auto 帧标记路径 = f_NE_get物理缓存路径((*it), S_节点数据::g流体缓存帧ID, (*it)->m_缓存区间, 0, false);
		if (f_phy_清除模拟帧标记((*it), 0, 0, 帧标记路径)) {
			
		}
	}
	f_时间轴_刷新绘制();
}




C_时间轴状态条::C_时间轴状态条(S_UI渲染环境& ctx) : C_状态条(ctx) {
	m_播放按钮 = f_alloc_EnumProp(nullptr,
		{ {u"前一帧", u"前一帧"}, {u"暂停", u"暂停"}, {u"播放", u"播放"}, {u"后一帧", u"后一帧"} },
		u"播放控制",
		0);
	f_prop_enum(m_播放按钮) = 1;

	m_当前帧 = f_alloc_I32Prop(&f_NodeCtx_get帧().当前帧, u"当前帧");
	m_帧区间 = f_alloc_iVec2Prop(&f_NodeCtx_get帧().帧区间, u"帧区间");
	
	m_播放按钮.m_私有 = true;
	m_当前帧.m_私有 = true;
	m_当前帧.m_私有 = true;
}

void C_时间轴状态条::f_重构() {
	std::vector<S_Props> props;
	S_Props	prop;

	prop = f_alloc_OpsProp(on_清除物理引擎缓存, u"清除缓存");
	props.push_back(prop);

	prop = f_alloc_NoneProp();
	props.push_back(prop);

	props.push_back(m_播放按钮);

	prop = f_alloc_NoneProp();
	props.push_back(prop);

	props.push_back(m_当前帧);
	props.push_back(m_帧区间);
	props.push_back(S_NodeCtx_模拟计算::g_是否开启解算);



	m_播放按钮.m_UI->m_Update = on_播放控制;
	m_当前帧.m_UI->m_Update = f_当前帧PropUpdate;
	m_帧区间.m_UI->m_Update = f_当前帧PropUpdate;


	f_构建属性部件(props, nullptr);
}



Inline static void f_update时间线绘制(C_Widget* self, C_时间轴* view) {
	auto* f = f_projectFile_取当前工程文件();
	auto frame = f_NodeCtx_get帧();
	return;
}

E_事件是否传递 f_时间轴页面鼠标点击(C_Widget* self, const E_鼠标按钮& 按钮) {
	C_时间轴& box = *dynamic_cast<C_时间轴*>(f_ui_get滚动框视口(self));
	f_时间轴页面工作区_拖拽时间帧(&box, E_键位执行状态::e_开始);
	
	f_时间轴_刷新绘制();
	
	f_工作区_联动更新(E_工作区类型::e_时间轴, E_工作区操作类型::e_所有);
	f_node_帧更新(true);

	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_时间轴页面鼠标滚动(C_Widget* self, const S_鼠标& 鼠标) {
	C_时间轴& box = *dynamic_cast<C_时间轴*>(f_ui_get滚动框视口(self));
	f_工作区_横向缩放页面视口(&box, E_键位执行状态::e_开始);
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_时间轴页面鼠标按下(C_Widget* self, const E_鼠标按钮& 按钮) {

	if (按钮 == E_鼠标按钮::e_鼠标中键) {
		g开始移动视口 = true;
	}

	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_时间轴页面鼠标放开(C_Widget* self, const E_鼠标按钮& 按钮) {
	C_时间轴* view = dynamic_cast<C_时间轴*>(self);

	if (按钮 == E_鼠标按钮::e_鼠标中键) {
		g开始移动视口 = false;
	}
	
	//g开始移动帧 = false;
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_时间轴页面鼠标位置改变(C_Widget* self, const ivec4& mouse) {
	C_时间轴& box = *dynamic_cast<C_时间轴*>(f_ui_get滚动框视口(self));
	//C_Widget* view = f_ui_get滚动框视口(self);

	vec2 globalLoc{ float32(mouse.x), float32(mouse.y)};
	static vec2 absLoc = globalLoc;
	if (g开始移动视口) {
		absLoc = globalLoc - absLoc;
	
		box.f_setX(box.m_Pos.x + absLoc.x);

		f_Widget_应用视口移动(self);
	}
	absLoc = globalLoc;
	
	return E_事件是否传递::e_事件传递_终止;
}



E_事件是否传递 f_时间轴页面_准备拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	data.m_Type = E_自定义拖放数据类型::e_拖放_时间线;

	if (鼠标.rgbButtons[E_鼠标按钮::e_鼠标左键]) {
		g开始移动帧 = true;
	} else if (鼠标.rgbButtons[E_鼠标按钮::e_鼠标中键]) {
		g开始移动视口 = true;
	}
	return E_事件是否传递::e_事件传递_继续;
}

E_事件是否传递 f_时间轴页面_手柄拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	if(data.m_Type != E_自定义拖放数据类型::e_拖放_时间线) return E_事件是否传递::e_事件传递_继续;
	//C_时间轴& box = *dynamic_cast<C_时间轴*>(self);
	C_时间轴& box = *dynamic_cast<C_时间轴*>(f_ui_get滚动框视口(self));


	vec2 globalLoc = 鼠标.lPos;

	static vec2 absLoc = globalLoc;
	if (g开始移动视口) {
		C_Widget* view = f_ui_get滚动框视口(self);

		absLoc = globalLoc - absLoc;
	}

	if (g开始移动帧) {
		f_时间轴页面工作区_拖拽时间帧(&box, E_键位执行状态::e_开始);
	}

	f_update时间线绘制(self, &box);
	absLoc = globalLoc;
	return E_事件是否传递::e_事件传递_继续;
}

bool f_时间轴页面_结束拖放(C_Widget* self, C_Widget* 拾取的部件, S_拖放数据& data) {
	
	//C_时间轴* view = dynamic_cast<C_时间轴*>(self);

	if (g开始移动帧) {
		g开始移动帧 = false;
	}
	if (g开始移动视口) {
		g开始移动视口 = false;
	}
	return true;
}


void f_时间轴动画(C_Widget* self) {
	//if (g时间更新绘制更新) {
	//	C_时间轴* view = dynamic_cast<C_时间轴*>(f_ui_get滚动框视口(self));
		//f_update时间线绘制(self, view);
	//}
}






static void on_时间轴布局(C_Widget* self, C_Widget** w, uint32 num) {
	C_时间轴* view = dynamic_cast<C_时间轴*>(self);

	//f_Widget_保持偏移比例(self);

	auto& 帧 = f_NodeCtx_get帧();
	auto& time = 帧.帧区间;
	float32 帧数量 = float32(time.y - time.x);

	//self->f_setW(帧数量 * S_时间轴帧绘制信息::g时间区间缩放);
}


static void on_时间轴绘制(C_Widget* self, S_2D画布* 画布) {
	C_时间轴& box = *dynamic_cast<C_时间轴*>(self);
	box.m_背景 = f_vg_genConvex((*self), *画布, E_填充模式::e_填充面, E_图层混合模式::e_Normal);
	box.m_刻度线 = f_vg_drawTickMark((*self), *画布, E_图层混合模式::e_Normal);
	box.m_时间数字 = f_vg_genString((*self), *画布, E_填充模式::e_填充面, E_图层混合模式::e_Normal, u"雅黑");
	box.m_时间线绘制 = f_vg_genLine((*self), *画布, E_图层混合模式::e_Normal, E_线类型::e_线段);

	box.m_当前时间字符背景 = f_vg_genConvex((*self), *画布, E_填充模式::e_填充面, E_图层混合模式::e_Normal);
	box.m_当前时间字符 = f_vg_genString((*self), *画布, E_填充模式::e_填充面, E_图层混合模式::e_Normal, u"雅黑");

}

static void on_时间轴变换(C_Widget* self) {
	C_时间轴& box = *dynamic_cast<C_时间轴*>(self);
	auto& 帧 = f_NodeCtx_get帧();

	auto* f = f_projectFile_取当前工程文件();

	int32 方向 = E_方向::e_纵向;
	
	S_2DConvexRect	区域[4]{};
	S_GPU内存块*	形状[4];
	S_RGBA8UI		颜色[4];
	vec2			位置[2];


	auto& time = 帧.帧区间;

	const float32 dpi = self->m_UIctx->m_DPI;
	const float32 帧数量 = float32(time.y - time.x);
	const float32 标签高度 = 20 * dpi;


	float32 跨度 = S_时间轴帧绘制信息::g时间区间缩放 * box.m_Scale.x;
	
	float32 刻度数值跨度 = 1 / box.m_Scale.x;
	int32 缩放比 = max(刻度数值跨度, 1);
	缩放比 = pow(2, int32(log2(缩放比)));

	float32 跨度1 = 跨度 * 缩放比;

	uint32 num1;
	int32 remainder = 缩放比 <= 1 ? 1 : 缩放比*5;
	if (box.m_Scale.x > 1) {
		缩放比 = pow(2, int32(log2(box.m_Scale.x)));
		remainder = max(5 / 缩放比, 1);
		if(缩放比 == 1) {
			跨度1 = 跨度;
		}
		else if(缩放比 == 2) {
			跨度1 = 跨度 * 2 / 5;
		}
		else {
			跨度1 = 跨度 * 2 / 10;
		}
	}
	else {
		remainder = 缩放比*5;
	}
	num1 = 帧数量 / remainder;
	

	vec2 offset{0, 标签高度 };
	位置[0] = {time.x * 跨度1, 标签高度};
	区域[0].size = {跨度1 * num1, self->m_Size.y};
	形状[0] = &(box.m_UIctx->m_Ctx.m_几何图形->m_平面矩形);
	颜色[0] = S_主题颜色::uic_时间轴时间区;

	//区域[1].offset = {};
	//区域[1].size = {box.m_Size.x, 标签高度};
	//形状[1] = &(box.m_UIctx->m_Ctx.m_几何图形->m_平面矩形);
	//颜色[1] = S_主题颜色::uic_时间轴时间区 - 20;

	f_vg_tranform(*box.m_背景, 1, 位置);
	f_vg_drawConvex(*box.m_背景, 1, 形状, 区域);
	f_vg_color(*box.m_背景, 1, 颜色);





	float32 帧位置 = 跨度1 * 帧.当前帧 / remainder;
	形状[0] = &(box.m_UIctx->m_Ctx.m_几何图形->m_平面纵向直线);
	颜色[0] = S_主题颜色::uic_当前时间线;
	f_vg_tranform(*box.m_时间线绘制, {帧位置, 0}, {1, box.m_Size.y});
	f_vg_setLine(*box.m_时间线绘制, 1, 形状, 2);
	f_vg_color(*box.m_时间线绘制, 1, 颜色);

	
	f_vg_tranform(*box.m_刻度线, offset);
	f_vg_color(*box.m_刻度线, 1, &S_节点UI主题::uic_刻度线L1);
	f_vg_setTickMark(*box.m_刻度线, num1 * 2 + 1, {跨度1 * 0.5f, 0}, box.m_Size - vec2{0, 标签高度}, DEF_TickMark_V);



	float32 编号区域宽度 = 跨度1;


	++num1;
	std::vector<std::u16string> 刻度文本(num1);
	std::vector<S_Rect2Df>		刻度区域(num1);
	std::vector<S_RGBA8UI>		刻度颜色(num1);
	for (uint32 i = 0; i < num1; ++i) {
		
		刻度文本[i] = f_整数值转宽字符串(int32(remainder * i));
		刻度区域[i] = {{i*编号区域宽度 - 编号区域宽度*0.5f, 0}, { 编号区域宽度, 标签高度 }};
		刻度颜色[i] = {100,100,100,255};
	}
	f_vg_drawString(*box.m_时间数字, 刻度文本, 刻度区域, 刻度颜色, E_对齐方式::e_据中对齐, {S_UI主题::ui_文字间隔, E_方向::e_横向 });
	f_vg_tranform(*box.m_时间数字, {});


	auto str_当前帧 = f_整数值转宽字符串(帧.当前帧);
	auto w = max(f_vg_getStringSize(*box.m_当前时间字符, str_当前帧, 标签高度, 2 * dpi, E_方向::e_横向), 10);
	w += f_vg_StringDrawOffset(标签高度) * 4;
	帧位置 -= w*0.5f;
	区域[0].offset = {0, 0};
	位置[0] = {帧位置, 0};
	区域[0].size = { w, 标签高度 };
	区域[0].radius = {3,3};
	形状[0] = &(box.m_UIctx->m_Ctx.m_几何图形->m_平面圆角矩形);
	颜色[0] = S_主题颜色::uic_当前时间线;
	f_vg_tranform(*box.m_当前时间字符背景, 1, 位置);
	f_vg_drawConvex(*box.m_当前时间字符背景, 1, 形状, 区域);
	f_vg_color(*box.m_当前时间字符背景, 1, 颜色);

	f_vg_drawString(*box.m_当前时间字符, str_当前帧, {{帧位置, 0}, { w, 标签高度 }}, S_UI主题::uic_文字颜色, E_对齐方式::e_据中对齐, {S_UI主题::ui_文字间隔, E_方向::e_横向 });
}


C_时间轴::C_时间轴(S_UI渲染环境& ctx) : C_Widget(&ctx) {
	mf_布局	= on_时间轴布局;
	mf_绘制	= on_时间轴绘制;
	mf_变换	= on_时间轴变换;
	f_widget_添加绘制画布(this);

	f_setSize({ 256, 256 });
	m_线数量 = 1;
	m_帧数字步进 = 1;
	m_帧步进宽度 = 1;

	C_时间轴::g_当前时间轴页面.insert(this);
	mui_状态条 = nullptr;
}

C_时间轴::~C_时间轴() {
	C_时间轴::g_当前时间轴页面.erase(this);
}


C_时间轴* f_创建时间轴页面(S_UI渲染环境& ctx) {
	C_时间轴* 时间轴 = new C_时间轴(ctx);
	f_widget_开启视口属性(时间轴);
	时间轴->m_扩展视口属性->m_大小 = { 1024, 256 };

	时间轴->mui_状态条 = new C_时间轴状态条(ctx);

	g_时间轴状态条.insert(时间轴->mui_状态条);

	return 时间轴;
}

E_事件是否传递 f_时间轴页面工作区_拖拽时间帧(C_Widget* self, E_键位执行状态 状态) {
	C_时间轴& box = *(C_时间轴*)self;
	f_时间工作区_从鼠标位置计算时间帧(self);
	self->m_更新绘制属性 = true;

	f_时间轴_刷新绘制();
	f_工作区_联动更新(E_工作区类型::e_时间轴, E_工作区操作类型::e_所有);
	f_node_帧更新(true);

	return E_事件是否传递::e_事件传递_终止;
}






